/*------------------------------------------------------------------------
    File        : BufferHelper
    Purpose     : 
    Syntax      : 
    Description : 
    @author pjudge
    Created     : Thu Mar 19 15:16:47 EDT 2009
    Notes       : 
  ----------------------------------------------------------------------*/
routine-level on error undo, throw.

using OpenEdge.Core.System.InvalidValueSpecifiedError.
using OpenEdge.Core.System.BufferNotAvailableError.
using OpenEdge.Core.System.BufferFieldMismatchError.

using OpenEdge.Lang.OperatorEnum.
using OpenEdge.Lang.CompareStrengthEnum.
using OpenEdge.Lang.DataTypeEnum.

class OpenEdge.Core.Util.BufferHelper: 
    
    method public static character extent BufferCompare (phSource as handle,
                                                         phTarget as handle):
        define variable iLoop          as integer   no-undo.
        define variable iField         as integer   no-undo.
        define variable hSourceField   as handle    no-undo.
        define variable hTargetField   as handle    no-undo.
        define variable iExtent        as integer   no-undo.
        define variable lExtent        as logical   no-undo.
        define variable iNumItems      as integer   no-undo.
        define variable iNumDiff       as integer   no-undo.
        define variable cChangedFields as character extent no-undo.
        define variable cTempFields    as character extent no-undo.
        
        if not valid-handle(phSource) then
            undo, throw new InvalidValueSpecifiedError('source buffer').
                        
        if not phSource:available then
            undo, throw new BufferNotAvailableError(phSource:name).

        if not valid-handle(phTarget) then
            undo, throw new InvalidValueSpecifiedError('target buffer').
                
        if not phTarget:available then
            undo, throw new BufferNotAvailableError(phTarget:name).
        
        /* max size is num fields in buffer */
        extent(cTempFields) = phSource:num-fields.
        
        do iField = 1 to phSource:num-fields:
            hSourceField = phSource:buffer-field(iField).
            hTargetField = phTarget:buffer-field(hSourceField:name) no-error.
    
            if not valid-handle(hTargetField) then
                undo, throw new BufferFieldMismatchError(phSource:Name, phTarget:Name).
    
            assign lExtent   = hSourceField:extent gt 0
                   iNumItems = if not lExtent then 1 else hSourceField:extent.
            
            do iExtent = 1 to iNumItems:
                if not CompareFieldValues(hSourceField,
                                          if lExtent then iExtent else 0,
                                          OperatorEnum:IsEqual,
                                          hTargetField,
                                          if lExtent then iExtent else 0,
                                          CompareStrengthEnum:Raw ) then 
                    assign iNumDiff = iNumDiff + 1
                           cTempFields[iNumDiff] = hSourceField:name
                                                 + if lExtent then "[" + string(iExtent) + "]" else "".
            end.  /* extent loop */
        end. /* field loop */

        extent(cChangedFields) = iNumDiff.
        do iField = 1 to iNumDiff:
            cChangedFields[iField] = cTempFields[iField].
        end.
                        
        return cChangedFields.
    end method.
        
    method protected static logical CompareFieldValues ( phColumn1  as handle,        
                                                         piExtent1  as integer,
                                                         poOperator as OperatorEnum,
                                                         phColumn2  as handle,
                                                         piExtent2  as integer,
                                                         poStrength as CompareStrengthEnum):
        define variable lSame   as logical no-undo.
        define variable iExtent as integer no-undo.
             
        if DataTypeEnum:Clob:Equals(phColumn1:data-type) then
            lSame = CompareClobValues(phColumn1,
                                      poOperator,
                                      phColumn2,
                                      poStrength).   
        else
        if DataTypeEnum:Character:Equals(phColumn1:data-type) then
            lSame = compare(phColumn1:buffer-value(piExtent1),
                            poOperator:ToString(),
                            phColumn2:buffer-value(piExtent2),
                            poStrength:ToString()).
        else
        do:
            case poOperator:
                when OperatorEnum:IsEqual then
                    lSame = phColumn1:buffer-value(piExtent1) eq phColumn2:buffer-value(piExtent2).
                when OperatorEnum:LessThan then
                    lSame = phColumn1:buffer-value(piExtent1) lt phColumn2:buffer-value(piExtent2).
                when OperatorEnum:LessEqual then
                    lSame = phColumn1:buffer-value(piExtent1) le phColumn2:buffer-value(piExtent2).
                when OperatorEnum:GreaterEqual then
                    lSame = phColumn1:buffer-value(piExtent1) ge phColumn2:buffer-value(piExtent2).
                when OperatorEnum:GreaterThan then
                    lSame = phColumn1:buffer-value(piExtent1) gt phColumn2:buffer-value(piExtent2).
                when OperatorEnum:NotEqual then
                    lSame = phColumn1:buffer-value(piExtent1) ne phColumn2:buffer-value(piExtent2).
            end case.
        end.
                        
        return lSame.
    end method.
              
    method protected static logical CompareClobValues ( phColumn1  as handle,                
                                                        poOperator as OperatorEnum,
                                                        phcolumn2  as handle,
                                                        poStrength as CompareStrengthEnum) :
        define variable cLong1    as longchar no-undo.
        define variable cLong2    as longchar no-undo.
        define variable lUnknown1 as logical  no-undo.
        define variable lUnknown2 as logical  no-undo.
        define variable lEqual    as logical  no-undo.
        define variable lCompare  as logical  no-undo.
    
        assign lEqual    = poOperator eq OperatorEnum:IsEqual
               lUnknown1 = (phColumn1:buffer-value eq ?)
               lUnknown2 = (phColumn2:buffer-value eq ?).
    
        if lUnknown1 and lUnknown2 then
            lCompare = lEqual.
        else
        if lUnknown1 or lUnknown2 then
            lCompare = not lEqual.
        else
        if length(phColumn1:buffer-value) ne length(phColumn2:buffer-value) then
            lCompare = not lEqual.
        else
        do:
            copy-lob from phColumn1:buffer-value to cLong1.
            copy-lob from phColumn2:buffer-value to cLong2.
            lCompare = compare(cLong1,
                               poOperator:ToString(),
                               cLong2,
                               poStrength:ToString()).
        end.
            
        return lCompare.
    end method.
end class.
